package com.main.tripplanner.algorithms.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.main.tripplanner.algorithms.CityAttractionsOpt;
import com.main.tripplanner.algorithms.DataRetrievalOpt;
import com.main.tripplanner.algorithms.RoutePlanningOpt;
import com.main.tripplanner.dao.AttractionQuestionDAO;
import com.main.tripplanner.dao.DosageDAO;
import com.main.tripplanner.dao.RouteDAO;
import com.main.tripplanner.persistent.Attraction;
import com.main.tripplanner.persistent.Attraction.AttractionType;
import com.main.tripplanner.persistent.AttractionQuestion;
import com.main.tripplanner.persistent.City;
import com.main.tripplanner.persistent.Country;
import com.main.tripplanner.persistent.Dosage;
import com.main.tripplanner.persistent.Route;
import com.main.tripplanner.service.AttractionService;
import com.main.tripplanner.service.CityService;
import com.main.tripplanner.service.RouteService;

@Service
public class RoutePlanningOptImpl implements RoutePlanningOpt{

	@Autowired
	private AttractionService attractionService;
	@Autowired
	private CityService cityService;
	@Autowired
	private RouteService routeService;
	@Autowired
	private CityAttractionsOpt cityAttractionsOpt;
	@Autowired
	private DataRetrievalOpt dataRetrievalOpt;
	@Autowired
	private RouteDAO routeDAO;
	@Autowired
	private DosageDAO dosageDAO;
	@Autowired
	private AttractionQuestionDAO attractionQuestionDAO;
	
	public Route planRoute(Country country, Dosage desiredDosage, float travelTime){
		Route route = new Route();
		boolean update = false;
		//we allow only one country per one route. if there's a route there, we return it.
		Route existedRoute = routeDAO.getRouteByCountry(country);
		if(existedRoute != null){
			if(!existedRoute.isToReplan()){
				existedRoute = routeDAO.getFullyInitializedRoute(existedRoute.getId());
				return existedRoute;
			}
			else{
				route = existedRoute;
				route.setCities(new HashSet<City>());
				route.setAttractions(new HashSet<Attraction>());
				route.setCurrentTime(0);
				update= true;
			}
		}
		//attraction to replace and best candidate.
		route.setCountry(country);
		route.setTotalTravelTime(travelTime);
		route.setDesiredDosage(desiredDosage);
		while(travelTime > 0){
			AttractionType attractionType = findMaximalAttractionType(route);
			Attraction bestAttraction = attractionService.getAttractionMaxEnjoyment(country, route, attractionType);
			if(bestAttraction.equals(new Attraction())){
				break;
			}
			//save future question to the user - does it really the best attraction?
			Map<Attraction,Integer> attractionPotentialMap = dataRetrievalOpt.nextAttractionEnjoymentQuestion(route, bestAttraction);
			if(!attractionPotentialMap.isEmpty()){
				Iterator<Attraction> iter = attractionPotentialMap.keySet().iterator();
				Attraction potentialAttraction = iter.next();
				AttractionQuestion attractionQuestion = createAttractionQuestion(country, potentialAttraction, bestAttraction, attractionPotentialMap.get(potentialAttraction));
				attractionQuestionDAO.saveEntity(attractionQuestion);
			}
			
			City city = bestAttraction.getCity();
			List<Attraction> cityAttractions = cityAttractionsOpt.getOptimalCityAttractions(city, bestAttraction, desiredDosage);
			cityAttractions.add(bestAttraction);
			routeService.updateRoute(route, cityAttractions, city);
			travelTime -= route.getCurrentTime();
		}
		if(update){
			routeDAO.updateEntity(route);
		}
		else{
			routeDAO.saveEntity(route);
		}
		return route;
	}

	private AttractionQuestion createAttractionQuestion(Country country, Attraction potentialAttraction, Attraction bestAttraction,
			Integer numberOfQuestionsToReplace) {
		AttractionQuestion attractionQuestion = new AttractionQuestion();
		attractionQuestion.setCountry(country);
		attractionQuestion.setBestCandidateToReplace(potentialAttraction);
		attractionQuestion.setCurrentAttraction(bestAttraction);
		attractionQuestion.setNumberOfQuestionsToReplace(numberOfQuestionsToReplace);
		return attractionQuestion;
	}

	private AttractionType findMaximalAttractionType(Route route) {
		Dosage desireDosage = route.getDesiredDosage();
		Dosage dosage = route.getDosage();
		
		float maxGap =0;
		AttractionType attractionType = null;
		Map<AttractionType, Float> dosageByAttractionType = mapDosageByAttractionType(dosage);
		Map<AttractionType, Float> desiredDosageByAttractionType = mapDosageByAttractionType(desireDosage);

		for (AttractionType attrType : dosageByAttractionType.keySet()) {
			float currentGap = desiredDosageByAttractionType.get(attrType) - dosageByAttractionType.get(attrType);
			if(maxGap < currentGap){
				maxGap = currentGap;
				attractionType = attrType;
			}
		}
		
		return attractionType;
	}

	private Map<AttractionType, Float> mapDosageByAttractionType(Dosage dosage) {
		Map<AttractionType, Float> result = new HashMap<AttractionType, Float>();
		result.put(AttractionType.CULTURE, dosage.getCulturePercentage());
		result.put(AttractionType.EXTREME, dosage.getExtremePercentage());
		result.put(AttractionType.FAMILY, dosage.getFamilyPercentage());
		result.put(AttractionType.NIGHT_LIFE, dosage.getNightLifePercentage());
		result.put(AttractionType.TREKKING, dosage.getTrekkingPercentage());
		return result;
	}
}
